package cn.iocoder.yudao.webapp;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.nio.file.Paths;
import java.sql.Types;

import static cn.iocoder.yudao.framework.constant.ConfigConstants.SYSTEM_USER_DIR;

/**
 * <pre>
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 *  基于 MyBatis-Plus + Freemarker 的代码生成器
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 * </pre>
 *
 * @author 山野羡民（1032694760@qq.com）
 * @since 2025/03/29
 */
public class MyBatisPlusGenerator {
    private static final String MODULE_NAME = "dine";//表前缀，即模块名
    private static final String TABLE_NAME = "dine_time_range";// 表名，即子模块名+具体功能名
    private static final String OUTPUT_DIR = Paths.get(SYSTEM_USER_DIR, "xianmin-codegen", "src", "main").toFile().getAbsolutePath();
    private static final String PACKAGE_NAME = "cn.iocoder.yudao.module";

    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mdycjt?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&autoReconnect=true&nullCatalogMeansCurrent=true", "root", "123456")
                .globalConfig(MyBatisPlusGenerator::buildGlobalConfig)
                .dataSourceConfig(MyBatisPlusGenerator::buildDataSourceConfig)
                .strategyConfig(MyBatisPlusGenerator::buildStrategyConfig)
                .packageConfig(MyBatisPlusGenerator::buildPackageConfig)
                .injectionConfig(MyBatisPlusGenerator::buildInjectionConfig)
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
        System.out.println("\n生成代码成功，存储路径：" + OUTPUT_DIR);
    }

    private static void buildGlobalConfig(GlobalConfig.Builder builder) {
        builder.author("山野羡民（1032694760@qq.com）")
                .commentDate("yyyy/MM/dd")
                .disableOpenDir()
                .dateType(DateType.TIME_PACK)
                .outputDir(OUTPUT_DIR + "/java/");
    }

    private static void buildDataSourceConfig(DataSourceConfig.Builder builder) {
        builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
            int typeCode = metaInfo.getJdbcType().TYPE_CODE;
            if (typeCode == Types.SMALLINT || typeCode == Types.TINYINT) {
                // 数据库 smallint 及 tinyint 类型统一映射 Java 的 Integer，而不是 Byte
                return DbColumnType.INTEGER;
            }
            return typeRegistry.getColumnType(metaInfo);
        });
    }

    private static void buildStrategyConfig(StrategyConfig.Builder builder) {
        builder.addInclude("%s".formatted(TABLE_NAME));
        builder.entityBuilder()
                .javaTemplate("/templates/entity.java")
                .enableFileOverride()
                .addIgnoreColumns("id", "creator", "create_time", "updater", "update_time", "tenant_id", "deleted")
                .formatFileName("%s");
        builder.mapperBuilder()
                .mapperTemplate("/templates/mapper.java")
                .enableFileOverride()
                .disableMapperXml()
                .formatMapperFileName("%sMapper");
        builder.serviceBuilder()
                .disableServiceImpl()
                .serviceTemplate("/templates/admin-service.java")
                .enableFileOverride()
                .formatServiceFileName("%sService");
        builder.controllerBuilder()
                .template("/templates/admin-controller.java")
                .enableFileOverride()
                //.enableHyphenStyle()
                .formatFileName("%sController");
    }

    private static void buildPackageConfig(PackageConfig.Builder builder) {
        String basePackageName = getBasePackageName();
        builder.moduleName(MODULE_NAME)
                .parent(PACKAGE_NAME)
                .entity("%s.pojo.po".formatted(basePackageName))
                .mapper("%s.dao".formatted(basePackageName))
                .service("%s.service.admin".formatted(basePackageName))
                .controller("%s.controller.admin".formatted(basePackageName));
    }

    private static void buildInjectionConfig(InjectionConfig.Builder builder) {
        String baseRouteName = getBaseRouteName();
        builder.beforeOutputFile((tableInfo, objectMap) -> {
            objectMap.put("viewObject", "%sVO".formatted(tableInfo.getEntityName()));
            objectMap.put("queryObject", "%sQueryDTO".formatted(tableInfo.getEntityName()));
            objectMap.put("saveObject", "%sSaveDTO".formatted(tableInfo.getEntityName()));
            objectMap.put("convertName", "%sConvert".formatted(tableInfo.getEntityName()));
            objectMap.put("basePackageName", "%s.%s.%s".formatted(PACKAGE_NAME, MODULE_NAME, getBasePackageName()));
            objectMap.put("mapperNameVar", StrUtil.lowerFirst(tableInfo.getMapperName()));
            objectMap.put("serviceNameVar", StrUtil.lowerFirst(tableInfo.getServiceName()));
            objectMap.put("routePath", "/%s/%s".formatted(MODULE_NAME, baseRouteName));
            objectMap.put("apiPath", "%s/%s/%sApi".formatted(MODULE_NAME, baseRouteName, tableInfo.getEntityName()));
            // hutool 的 JSONUtil 兼容性比较好
            System.out.println("tableInfo: \n" + JSONUtil.toJsonPrettyStr(tableInfo));
            System.out.println("objectMap: \n" + JSONUtil.toJsonPrettyStr(objectMap));
        });
        buildCustomFile(builder);
    }

    private static void buildCustomFile(InjectionConfig.Builder builder) {
        String basePackageName = getBasePackageName();
        String javaOutputFilePath = OUTPUT_DIR + "/java/" + PACKAGE_NAME.replace(".", "/") + "/" + MODULE_NAME + "/" + basePackageName;
        String adminOutputFilePath = OUTPUT_DIR + "/admin/src";
        // 后端文件
        builder.customFile(newCustomFile("", "VO.java", "/templates/vo.java.ftl", javaOutputFilePath + "/pojo/vo/"))
                .customFile(newCustomFile("", "QueryDTO.java", "/templates/query-dto.java.ftl", javaOutputFilePath + "/pojo/dto/"))
                .customFile(newCustomFile("", "SaveDTO.java", "/templates/save-dto.java.ftl", javaOutputFilePath + "/pojo/dto/"))
                .customFile(newCustomFile("", "Convert.java", "/templates/convert.java.ftl", javaOutputFilePath + "/convert/"))
                .customFile(newCustomFile("app", "Service.java", "/templates/app-service.java.ftl", javaOutputFilePath + "/service/app/"))
                .customFile(newCustomFile("core", "Service.java", "/templates/core-service.java.ftl", javaOutputFilePath + "/service/core/"))
                .customFile(newCustomFile("open", "Service.java", "/templates/open-service.java.ftl", javaOutputFilePath + "/service/open/"))
                .customFile(newCustomFile("app", "Controller.java", "/templates/app-controller.java.ftl", javaOutputFilePath + "/controller/app/"))
                .customFile(newCustomFile("open", "Controller.java", "/templates/open-controller.java.ftl", javaOutputFilePath + "/controller/open/"))
                // 前端文件
                .customFile(newCustomFile("", "Api.ts", "/templates/api.ts.ftl", adminOutputFilePath + "/api/" + MODULE_NAME + "/" + basePackageName + "/"))
                .customFile(newCustomFile("", "Form.vue", "/templates/form.vue.ftl", adminOutputFilePath + "/views/" + MODULE_NAME + "/" + basePackageName + "/"))
                .customFile(newCustomFile("", "List.vue", "/templates/list.vue.ftl", adminOutputFilePath + "/views/" + MODULE_NAME + "/" + basePackageName + "/"));
    }

    private static String getBasePackageName() {
        return TABLE_NAME.replace(MODULE_NAME + "_", "").split("_")[0];
    }

    private static String getBaseRouteName() {
        return TABLE_NAME.replace(MODULE_NAME + "_", "").replaceAll("_", "/");
    }

    private static CustomFile newCustomFile(String prefix, String fileName, String templatePath, String outputFilePath) {
        return new CustomFile.Builder()
                .enableFileOverride()
                .templatePath(templatePath)
                .formatNameFunction(tableInfo -> StrUtil.format("{}{}{}",
                        StrUtil.isBlank(prefix) ? "" : StrUtil.upperFirst(prefix),
                        StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getEntityName())),
                        fileName))
                .fileName("")
                .filePath(outputFilePath)
                .build();
    }

}
